package com.cmc6.bulletin.service.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.thread.ThreadUtil;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cmc6.bulletin.mapper.BulletinMapper;
import com.cmc6.bulletin.model.dto.BulletinInsertOrUpdateDTO;
import com.cmc6.bulletin.model.dto.BulletinPageDTO;
import com.cmc6.bulletin.model.dto.BulletinUserPageDTO;
import com.cmc6.bulletin.model.entity.BulletinDO;
import com.cmc6.bulletin.model.entity.BulletinReadTimeRefUserDO;
import com.cmc6.bulletin.model.enums.BulletinStatusEnum;
import com.cmc6.bulletin.model.vo.BulletinPageVO;
import com.cmc6.bulletin.model.vo.BulletinUserPageVO;
import com.cmc6.bulletin.service.BulletinReadTimeRefUserService;
import com.cmc6.bulletin.service.BulletinService;
import com.cmc6.bulletin.task.BulletinPush;
import com.cmc6.common.constant.BaseConstant;
import com.cmc6.common.exception.BaseBizCodeEnum;
import com.cmc6.common.model.dto.NotEmptyIdSet;
import com.cmc6.common.model.dto.NotNullId;
import com.cmc6.common.model.entity.BaseEntityThree;
import com.cmc6.common.model.entity.BaseEntityTwo;
import com.cmc6.common.util.*;
import com.cmc6.xxl.job.model.dto.XxlJobInsertOrUpdateDTO;
import com.cmc6.xxl.job.service.XxlJobService;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.Collections;
import java.util.Date;

@Service
public class BulletinServiceImpl extends ServiceImpl<BulletinMapper, BulletinDO> implements BulletinService {

    @Resource
    XxlJobService xxlJobService;
    @Resource
    RedissonClient redissonClient;
    @Resource
    BulletinReadTimeRefUserService bulletinReadTimeRefUserService;

    /**
     * 新增/修改 公告
     */
    @Override
    @Transactional
    public String insertOrUpdate(BulletinInsertOrUpdateDTO dto) {

        if (dto.getId() != null) {
            // 如果是修改，则加锁
            RLock lock =
                redissonClient.getLock(BaseConstant.PRE_REDISSON + BaseConstant.PRE_LOCK_BULLETIN_ID + dto.getId());
            lock.lock();
            try {
                doInsertOrUpdate(dto);
            } finally {
                lock.unlock();
            }
        } else {
            doInsertOrUpdate(dto);
        }

        return BaseBizCodeEnum.API_RESULT_OK.getMsg();
    }

    /**
     * 执行：新增/修改 公告
     */
    private void doInsertOrUpdate(BulletinInsertOrUpdateDTO dto) {

        if (dto.getId() != null) {
            Long count =
                lambdaQuery().eq(BaseEntityTwo::getId, dto.getId()).eq(BulletinDO::getStatus, BulletinStatusEnum.DRAFT)
                    .count();
            if (count != 1) {
                ApiResult.error("操作失败：只能修改草稿状态的公告");
            }
        }

        BulletinDO bulletinDO = new BulletinDO();
        bulletinDO.setType(dto.getType());
        bulletinDO.setContent(dto.getContent());
        bulletinDO.setTitle(dto.getTitle());
        bulletinDO.setSendTime(dto.getSendTime());
        bulletinDO.setStatus(BulletinStatusEnum.DRAFT); // 新增/修改 不能修改【公告状态】
        bulletinDO.setRemark(MyEntityUtil.getNotNullStr(dto.getRemark()));
        bulletinDO.setId(dto.getId());
        bulletinDO.setXxlJobId(MyEntityUtil.getNotNullLong(null));

        saveOrUpdate(bulletinDO);

    }

    /**
     * 提交 公告
     */
    @Override
    @Transactional
    public String submit(NotNullId notNullId) {

        RLock lock =
            redissonClient.getLock(BaseConstant.PRE_REDISSON + BaseConstant.PRE_LOCK_BULLETIN_ID + notNullId.getId());
        lock.lock();
        try {

            BulletinDO bulletinDO = getById(notNullId.getId());

            if (BulletinStatusEnum.FORMAL.equals(bulletinDO.getStatus())) {
                ApiResult.error("操作失败：已经提交过了，请刷新重试");
            }

            // 判断发布时间是否晚于当前时间
            int compare = DateUtil.compare(bulletinDO.getSendTime(), new Date());
            if (compare < 0) {
                ApiResult.error("操作失败：发布时间晚于当前时间");
            }

            bulletinDO.setStatus(BulletinStatusEnum.FORMAL);

            // 增加一个定时任务，用于：让用户收到 socket
            XxlJobInsertOrUpdateDTO dto = new XxlJobInsertOrUpdateDTO();
            dto.setJobDesc("socket：有新的公告【" + bulletinDO.getTitle() + "】");
            dto.setCSendTime(bulletinDO.getSendTime());
            dto.setExecutorHandler(BulletinPush.BULLETIN_PUSH);
            Long xxlJobId = xxlJobService.insertOrUpdate(dto);

            // 设置：定时任务 主键id
            bulletinDO.setXxlJobId(xxlJobId);

            updateById(bulletinDO); // 操作数据库

            return BaseBizCodeEnum.API_RESULT_OK.getMsg();

        } finally {
            lock.unlock();
        }

    }

    /**
     * 撤回 公告
     */
    @Override
    @Transactional
    public String revoke(NotNullId notNullId) {

        RLock lock =
            redissonClient.getLock(BaseConstant.PRE_REDISSON + BaseConstant.PRE_LOCK_BULLETIN_ID + notNullId.getId());
        lock.lock();
        try {

            BulletinDO bulletinDO = getById(notNullId.getId());

            if (BulletinStatusEnum.DRAFT.equals(bulletinDO.getStatus())) {
                ApiResult.error("操作失败：已经撤回过了，请刷新重试");
            }

            // 判断发布时间是否晚于当前时间
            int compare = DateUtil.compare(bulletinDO.getSendTime(), new Date());
            if (compare < 0) {
                ApiResult.error("操作失败：发布时间晚于当前时间，无法撤回");
            }

            bulletinDO.setStatus(BulletinStatusEnum.DRAFT);

            // 删除提交时，创建的定时任务
            Long xxlJobId = bulletinDO.getXxlJobId();
            xxlJobService.deleteById(new NotNullId(xxlJobId));

            bulletinDO.setXxlJobId(MyEntityUtil.getNotNullLong(null)); // 设置 定时任务 主键id

            updateById(bulletinDO); // 操作数据库

            return BaseBizCodeEnum.API_RESULT_OK.getMsg();

        } finally {
            lock.unlock();
        }
    }

    /**
     * 分页排序查询：公告
     */
    @Override
    public Page<BulletinPageVO> myPage(BulletinPageDTO dto) {

        return baseMapper.myPage(dto.getPage(), dto);
    }

    /**
     * 通过主键id，查看详情
     */
    @Override
    public BulletinDO infoById(NotNullId notNullId) {

        return getById(notNullId.getId());
    }

    /**
     * 删除公告
     */
    @Override
    @Transactional
    public String deleteByIdSet(NotEmptyIdSet notEmptyIdSet) {

        // 设置连锁
        RLock multiLock = MultiLockUtil.getMultiLock(BaseConstant.PRE_LOCK_BULLETIN_ID, notEmptyIdSet.getIdSet());
        multiLock.lock();
        try {

            Long count = lambdaQuery().in(BaseEntityTwo::getId, notEmptyIdSet.getIdSet())
                .eq(BulletinDO::getStatus, BulletinStatusEnum.DRAFT).count();
            if (count != notEmptyIdSet.getIdSet().size()) {
                ApiResult.error("操作失败：只能删除草稿状态的公告");
            }

            removeByIds(notEmptyIdSet.getIdSet());

            return BaseBizCodeEnum.API_RESULT_OK.getMsg();

        } finally {
            multiLock.unlock();
        }

    }

    /**
     * 分页排序查询：当前用户可以查看的公告
     */
    @Override
    public Page<BulletinUserPageVO> userPage(BulletinUserPageDTO dto) {

        Long userIdSafe = UserUtil.getCurrentUserIdSafe();

        ThreadUtil.execute(() -> {
            BulletinReadTimeRefUserDO bulletinReadTimeRefUserDO = new BulletinReadTimeRefUserDO();
            bulletinReadTimeRefUserDO.setUserId(userIdSafe);
            bulletinReadTimeRefUserDO.setBulletinReadTime(new Date());
            Long count =
                bulletinReadTimeRefUserService.lambdaQuery().eq(BulletinReadTimeRefUserDO::getUserId, userIdSafe)
                    .count();
            if (count == 0) {
                bulletinReadTimeRefUserService.save(bulletinReadTimeRefUserDO);
            } else {
                bulletinReadTimeRefUserService.updateById(bulletinReadTimeRefUserDO);
            }

            // 通知该用户，刷新 公告信息
            KafkaUtil.bulletinPush(Collections.singleton(userIdSafe));
        });

        return baseMapper.userPage(dto.getPage(), dto);
    }

    /**
     * 获取：当前用户可以查看的公告，总数
     */
    @Override
    public Long userCount() {

        Long userIdSafe = UserUtil.getCurrentUserIdSafe();
        BulletinReadTimeRefUserDO bulletinReadTimeRefUserDO =
            bulletinReadTimeRefUserService.lambdaQuery().eq(BulletinReadTimeRefUserDO::getUserId, userIdSafe)
                .select(BulletinReadTimeRefUserDO::getBulletinReadTime).one();

        LambdaQueryChainWrapper<BulletinDO> queryChainWrapper =
            lambdaQuery().eq(BaseEntityThree::getDelFlag, false).eq(BulletinDO::getStatus, BulletinStatusEnum.FORMAL)
                .le(BulletinDO::getSendTime, new Date());

        if (bulletinReadTimeRefUserDO != null) {
            queryChainWrapper.ge(BulletinDO::getSendTime, bulletinReadTimeRefUserDO.getBulletinReadTime());
        }
        return queryChainWrapper.count();
    }

}




